#!/usr/bin/python3
# -*- coding: utf-8 -*-
import traceback

from api import zabbix_api62, netbox_api
from api.universal import change_datetime
import urllib3

# logger = logging.getLogger('netbox')
# 忽略https告警
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

updatehosts = 0
addhosts = 0
x_name = ''


from loguru import logger
logger.add("/var/log/Task/netbox/update_hosts.log", rotation="00:00", backtrace=True, diagnose=True, level="DEBUG")


# --------zabbix 创建主机函数-----------
def zabbix_create_host(host_data, hostIP, group_id, template_id, port_type, macros, netbox_id, inventory, tags,
                       host_status, forcibly_update, token, zabbix_server_url, netbox_token, stack):
    """
	:param hostname:    主机名
	:param hostIP:  主机IP
	:param group_id:
	:param template_id:  
	:param port_type:  监控端口类型，主要为snmp以及agent
	:param macros:  自定义用户宏
	:param netbox_id:  netboxID
	:param inventory:  资产表
	:param tags:  tag信息
	:param host_status:  host状态
	:param forcibly_update: #强制更新开关，默认0，校验netbox与zabbix最后更新时间，1为不校验，强制更新
	:return:
	"""
    # 强制更新开关
    # forcibly_update=1

    try:

        global addhosts
        ret = ''
        # 检索zabbix是否有这台host
        if not netbox_id:
            netbox_id = 0
        search_info = {
            "macro": "{$NETBOX_ID}",
            "value": "netbox_" + str(netbox_id) + "_end"
        }
        # 查询netbox_id与hostname
        search_ret = zabbix_api62.zabbix_get_macros(token, zabbix_server_url, 'search', search_info)
        search_ret2 = zabbix_api62.zabbix_get_hosts(token, zabbix_server_url, 'host', host_data['name'])
        if 'result' in search_ret:
            if not len(search_ret['result']) and not len(search_ret2['result']):
                if host_data['status']["label"] == "Active" or host_data['status']["label"] == "Staged" or \
                        host_data['status']["label"] == "Planned":
                    logger.info("正在创建zabbix_host:" + host_data['name'])
                    resp_data = zabbix_api62.zabbix_create_host(token, zabbix_server_url, hostIP, host_data['name'], group_id,
                                                                template_id,
                                                                port_type, macros, inventory, tags, host_status)
                    if resp_data.get("status") == "Error":
                        return resp_data
                    resp_data["hostname"] = host_data['name']
                else:
                    resp_data = {"status": "None", "hostname": host_data['name'],
                                 "message": f"该设备状态为{host_data['status']['label']}，不进行创建",
                                 "detail": f"该设备状态为{host_data['status']['label']}，不进行创建"}
                    return resp_data
            else:
                # 检查是否存在重名设备
                if not len(search_ret['result']) and len(search_ret2['result']):
                    search_ret = search_ret2
                logger.info("host" + host_data['name'] + "已存在,检查更新···")
                # logger.info("host" + hostname + "已存在,检查更新···")

                macros_ret = zabbix_api62.zabbix_get_macros(token, zabbix_server_url, 'hostids',
                                                            search_ret['result'][0]['hostid'])
                # 判断netbox线路更新时间是否比zabbix新
                netbox_id = ""
                for x in macros_ret['result']:
                    if x['macro'] == '{$UPDATA_DATE}':
                        x_date = x['value']
                        zabbix_date = change_datetime(x_date)
                    elif x["macro"] == "{$STACK}":
                        zabbix_stack = x["value"]
                    elif x["macro"] == "{$NETBOX_ID}":
                        netbox_id = x["value"]
                if netbox_id.startswith("netbox_virtual"):
                    resp_data = {"status": "None", "hostname": host_data['name'], "detail": "该主机现在为虚拟机，本次不更新"}
                    logger.info(resp_data)
                    return resp_data
                try:
                    zabbix_date and zabbix_stack
                except NameError:
                    resp_data = zabbix_update_host(search_ret['result'][0]['hostid'], hostIP, host_data['name'], '', group_id,
                                                   template_id,
                                                   port_type, macros, inventory, tags, host_status, token,
                                                   zabbix_server_url)

                else:
                    if stack and stack.startswith("member") and zabbix_stack and zabbix_stack.startswith("member"):
                        stack_member = int(stack.split("member")[-1].strip())
                        zabbix_stack_member = int(zabbix_stack.split("member")[-1].strip())
                        if (stack_member < zabbix_stack_member) or (
                                stack_member == zabbix_stack_member and last_date > zabbix_date) or forcibly_update == 1:
                            resp_data = zabbix_update_host(search_ret['result'][0]['hostid'], hostIP, host_data['name'], '', group_id,
                                               template_id,
                                               port_type, macros, inventory, tags, host_status, token,
                                               zabbix_server_url)
                        else:
                            resp_data = {"status": "None", "hostname": host_data['name'],
                                         "message": "不满足更新要求不进行更新", "detail": "更新时间、member、强制更新均不满足更新要求"}
                    else:
                        if last_date > zabbix_date or forcibly_update == 1:
                            resp_data = zabbix_update_host(search_ret['result'][0]['hostid'], hostIP, host_data['name'], '',
                                                           group_id,
                                                           template_id,
                                                           port_type, macros, inventory, tags, host_status, token,
                                                           zabbix_server_url)
                        else:
                            resp_data = {"status": "None", "hostname": host_data['name'], "message": "未检测到更新", "detail": "设备未检测到更新"}
                            logger.info(host_data['name'] + '未检测到更新')
        else:
            resp_data = {"status": "None", "hostname": host_data['name']}
        logger.info(resp_data)
        return resp_data
    except Exception as e:
        resp_data = {"status": "Error", "message": "设备更新失败", "detail": traceback.format_exc(), "hostname": host_data['name']}
        logger.error(traceback.format_exc())
        return resp_data


# --------zabbix 更新主机函数-----------
def zabbix_update_host(hostid, hostip, hostname, name, group_id, templateid, port_type, macros, inventory, tags,
                       host_status, token, zabbix_server_url):
    try:
        global updatehosts
        logger.info('检测到host信息有更新，正在更新')
        resp_data = {
            "status": "Update",
            "hostname": hostname
        }
        groups = []
        for id in group_id:
            groups.append({"groupid": id})
        # 获取host接口ID
        zabbix_ret = zabbix_api62.zabbix_get_hostinterface(token, zabbix_server_url, hostid)
        # 更新监控非IP信息
        update_ret1 = zabbix_api62.zabbix_update_line(token, zabbix_server_url, hostid, hostip, hostname, name, groups,
                                                      templateid, port_type, macros, inventory, tags, host_status)
        if "error" in update_ret1 and update_ret1["error"]["data"].startswith("Item prototype") and \
                update_ret1["error"]["data"].endswith("linked to another rule."):
            template_info = zabbix_api62.zabbix_get_host_templatesid(zabbix_server_url, hostid, token)
            for i in template_info[0]["parentTemplates"]:
                clear_templateid = i["templateid"]
                clear_templates = zabbix_api62.zabbix_templates_clear(token, zabbix_server_url, hostid, name,
                                                                      clear_templateid)
            update_ret1 = zabbix_api62.zabbix_update_line(token, zabbix_server_url, hostid, hostip, hostname, name,
                                                          groups,
                                                          templateid, port_type, macros, inventory, tags, host_status)
        # 比较IP，如有更改，则更新IP
        if hostip != zabbix_ret['result'][0]['ip']:
            resp_data = zabbix_api62.zabbix_uptade_hostinterface(token, zabbix_server_url,
                                                                 zabbix_ret['result'][0]['interfaceid'], hostip)
            resp_data["hostname"] = hostname
            logger.info('line' + name + ' IP信息已更新')
        return resp_data
    except Exception as e:
        resp_data = {
            "status": "Error",
            "message": "更新主机失败",
            "detail": traceback.format_exc(),
            "hostname": hostname
        }
        logger.exception(traceback.format_exc())
        return resp_data


# zabbix 状态同步判断
def status_check(status):
    # 如果设备状态需要开启监控，则返回0，zabbixapi可直接调用，这里写法与zabbix_sync文件相反
    if status == 'Active' or status == 'Staged':
        return 0  # 0在zabbix里为active
    else:
        return 1  # 1在zabbix中为disable


# --------根据hosts列表创建host
def creat_hosts(x, netbox_token, verify, token, zabbix_server_url, father_template_group, netbox_url):
    global x_name, last_date
    try:
        status = "None"
        template_list = []
        # forcibly_update = 1  # 强制更新开关，为1则不对比更新时间戳
        # 将 name 对象解析为 name， 由接口返回结果知 name 是一个列表（数组）
        # 如果当前hostname与上个hostname相同（堆叠设备），则跳过此次循环，进入下一轮循环
        # 匹配需要同步的hosts
        # if x_name == x['name']:  # 同名设备（堆叠）则跳过
        #     resp_data = {
        #         "status": status,
        #         "hostname": x["name"],
        #         "message": "同名设备（堆叠）则跳过"
        #     }
        #     return resp_data
        # 判断设备状态
        host_status = status_check(x['status']['label'])

        x_name = x['name']
        # 获取办公区信息
        if x['site']['url'].startswith("http"):
            url = x['site']['url']
        else:
            url = netbox_url + x['site']['url']
        site_info = netbox_api.netbox_get_next(netbox_token, url, verify)
        logger.info(netbox_url + x['site']['url'])
        sitecode = site_info.get('slug')
        # 判断站点状态，如果站点offing，则不同步hosts
        if site_info['status']['value'] in ['retired']:
            logger.info('Site status is ' + str(site_info['status']['label']) + '(' + str(
                site_info['status']['value']) + '), No change.')
            # logger.info('Site status is ' + str(site_info['status']['label']) + '(' + str(
            #     site_info['status']['value']) + '), No change.')
            resp_data = {
                "status": status,
                "hostname": x["name"],
            }
            logger.info("站点状态offing， 不同步hosts")
            return resp_data
        # print(x)
        if x['tenant']:
            department = str(x['tenant']['name'])
        else:
            department = "None"
        last_updated = x['last_updated']
        last_date = change_datetime(last_updated)

        # 识别设备类型与级别，设置相应告警模板
        device_priority = x['custom_fields']['Priority']
        if not device_priority:
            device_priority = 'P4'
        device_type = x['device_role']['slug'] + ' ' + x['device_type']['manufacturer']['name']

        site_document = str(site_info['custom_fields'].get('SiteDocument'))
        stack = x["custom_fields"].get("Stack")
        # netbox信息同步至zabbix相关项
        macros = [
            {
                "macro": "{$NETBOX_ID}",
                "value": "netbox_" + str(x['id']) + "_end"
            },
            {
                "macro": "{$UPDATA_DATE}",
                "value": last_updated
            },
            {
                "macro": "{$SITE_CODE}",
                "value": str(sitecode)
            },
            {
                "macro": "{$SITE_NAME}",
                "value": str(site_info['name'])
            },
            {
                "macro": "{$SITE_STATUS}",
                "value": site_info['status']['label'],
                "description": str(site_info['status']['value'])
            },
            {
                "macro": "{$SITE_DOCUMENT}",
                "value": site_document
            },
            {
                "macro": "{$DEVICE_MODEL}",
                "value": str(x['device_type']['model'])
            },
            {
                "macro": "{$DEVICE_STATUS}",
                "value": str(x['status']['label']),
                "description": str(x['status']['value'])
            },
            {
                "macro": "{$DEVICE_ROLE}",
                "value": 'host_' + str(x['device_role']['slug']),
            },
            {
                "macro": "{$DEPARTMENT}",
                "value": str(department)
            },
            {
                "macro": "{$STACK}",
                "value": str(stack)
            },
            {
                "macro": "{$NETBOX_MODULE}",
                "value": "device",
            }
        ]
        inventory = {
            "poc_1_name": department,
            "location": site_info.get('physical_address'),
            "location_lat": site_info.get('latitude'),
            "location_lon": site_info.get('longitude')
        }
        tags = [
            {
                "tag": "department",
                "value": str(department)
            },
            {
                "tag": "device_type",
                "value": str(device_type)
            },
            {
                "tag": "device_model",
                "value": str(x['device_type']['model']),
            },
            {
                "tag": "device_status",
                "value": str(x['status']['label'])
            },
            {
                "tag": "sitecode",
                "value": str(sitecode)
            },
            {
                "tag": "site_status",
                "value": str(site_info['status']['label']),
            },
            {
                "tag": "device_role",
                "value": 'host_' + str(x['device_role']['slug'])
            },
            {
                "tag": "stack",
                "value": str(stack)
            },
            {
                "tag": "netbox_module",
                "value": "device",
            }
        ]
        # 根据hostname机房标签增加机房tag与宏
        if '-M-' in x['name']:
            tags.append({
                "tag": "df_type",
                "value": 'MDF'
            }, )
            macros.append({
                "macro": "{$DF_TYPE}",
                "value": 'MDF'
            })
        elif any(i in x['name'] for i in ['-A-', '-B-', '-C-', '-D-', '-E-', '-F-']):
            tags.append({
                "tag": "df_type",
                "value": 'IDF'
            }, )
            macros.append({
                "macro": "{$DF_TYPE}",
                "value": 'IDF'
            })
        elif '-W-UPS' in x['name']:
            forcibly_update = 1  # 强制更新开关，为1则不对比更新时间戳
            tags.append({
                "tag": "df_type",
                "value": 'workspace'
            }, )
            macros.append({
                "macro": "{$DF_TYPE}",
                "value": 'workspace'
            })
        else:
            tags.append({
                "tag": "df_type",
                "value": 'unknow'
            }, )
            macros.append({
                "macro": "{$DF_TYPE}",
                "value": 'unknow'
            })
        template = device_priority + ' ' + device_type
        group_name = (x['site']['slug'] + '-' + x['site']['name'])
        group2_name = template
        group3_name = str(x['device_type']['model'])
        host_name = x['name']

        # 检索group_id
        group_info = zabbix_api62.zabbix_get_group(token, zabbix_server_url, 'name', group_name + '*', False)
        group2_info = zabbix_api62.zabbix_get_group(token, zabbix_server_url, 'name', group2_name + '*', False)
        group3_info = zabbix_api62.zabbix_get_group(token, zabbix_server_url, 'name', group3_name + '*', False)

        # 判断主机类型选择接口
        if x['device_type']['manufacturer']['slug'] in ['linux', 'tardetech', "ZBXP"]:
            port_type = 1  # agent
        else:
            port_type = 2  # snmp
        # 检测设备类型分组，如未找到则创建
        if group2_info and type(group2_info) != dict:
            group2_id = group2_info[0].get('groupid')
        else:
            ret = zabbix_api62.zabbix_create_group(token, zabbix_server_url, group2_name)
            logger.info(ret)
            group2_id = ret['result']['groupids'][0]

        if len(group3_info) > 0:
            group3_id = group3_info[0].get('groupid')
        else:
            ret = zabbix_api62.zabbix_create_group(token, zabbix_server_url, group3_name)
            logger.info(ret)
            group3_id = ret['result']['groupids'][0]
        # 检测站点分组，如未找到则跳过
        if group_info:
            group_id_list = [group_info[0]['groupid'], group2_id, group3_id]  # 模板分组, 添加设备角色分组, 添加设备类型分组
            if department == 'ITOB':
                itob_group = zabbix_api62.zabbix_get_group(token, zabbix_server_url, 'name', '7000-ITOB-Devices', False)
                group_id_list.append(itob_group[0]['groupid'])
            elif department == 'ITSEC':
                itsec_group = zabbix_api62.zabbix_get_group(token, zabbix_server_url, 'name', '8000-ITSEC-Devices',
                                                            False)
                group_id_list.append(itsec_group[0]['groupid'])
            # 检索标准模板template_id
            template_info = zabbix_api62.zabbix_get_templates(token, zabbix_server_url, 'host', template)
            # print(template_info)
            if template_info:
                template_id = template_info[0]['templateid']
            else:
                logger.info('未找到模板,创建中:' + template)
                # tmp_group_info = zabbix_api62.zabbix_get_group(token, zabbix_server_url, 'name', father_template_group, False)
                tmp_group_info = zabbix_api62.zabbix_get_templategroup(token, zabbix_server_url, father_template_group, False)
                if len(tmp_group_info):
                    tmp_group_id = tmp_group_info[0]['groupid']
                    template_info = zabbix_api62.zabbix_create_template(token, zabbix_server_url, tmp_group_id,
                                                                        template)
                    template_id = template_info['templateids'][0]
                else:
                    logger.info(f'error:请创建templates group:{father_template_group}')
                    status = "Error"
                    resp_data = {"status": status, "message": "更新失败",
                                 "detail": f'error:请创建templates group:{father_template_group}',
                                 "hostname": host_name}
                    return resp_data
            # 扩展模板处理
            if x["custom_fields"]["ExtendedTemplate"]:
                template_list.append({
                    "templateid": template_id
                })
                expand_template_group = "Template Service Group"
                for extemplate_name in x["custom_fields"]["ExtendedTemplate"]:
                    template_info = zabbix_api62.zabbix_get_templates(token, zabbix_server_url, 'host', extemplate_name)
                    if template_info:
                        template_list.append({
                            "templateid": template_info[0]['templateid']
                        })
                    else:
                        logger.info(f'未找到模板{extemplate_name},检查模板组: {expand_template_group}')
                        tmp_group_info = zabbix_api62.zabbix_get_group(token, zabbix_server_url, 'name',
                                                                       expand_template_group, False)
                        if len(tmp_group_info):
                            logger.info("模板组正常")
                            ex_group_id = tmp_group_info[0]['groupid']
                        else:
                            logger.info(f"未找到模版组{expand_template_group},创建中···")
                            # 创建扩展模板组
                            create_group_ret = zabbix_api62.zabbix_create_group(token, zabbix_server_url,
                                                                                expand_template_group,
                                                                                verify)
                            ex_group_id = create_group_ret["result"]["groupids"][0]
                        # 创建扩展模板
                        logger.info(f'创建模板:{extemplate_name}')
                        template_info = zabbix_api62.zabbix_create_template(token, zabbix_server_url, ex_group_id,
                                                                            extemplate_name)
                        logger.info(template_info)
                        template_list.append({
                            "templateid": template_info['templateids'][0]
                        })

            # 调用主机创建函数
            managementip = x['custom_fields']['ManagementIP']
            if template_list:
                template_id = template_list
            resp_data = zabbix_create_host(x, managementip, group_id_list, template_id, port_type, macros,
                                           x['id'], inventory, tags, host_status, 0, token, zabbix_server_url,
                                           netbox_token, stack)

        else:
            logger.info('未找到分组:' + group_name)
            resp_data = {"status": "Error", "message": "设备更新失败", "detail": f"未找到分组{group_name}", "hostname": host_name}
        return resp_data
    except Exception as e:
        resp_data = {
            "status": "Error",
            "message": "更新失败",
            "detail": traceback.format_exc(),
            "hostname": x["name"]
        }
        logger.exception(traceback.format_exc())
        return resp_data
